import {
    EventDispatcher,
    Matrix4,
    Plane,
    Raycaster,
    Vector2,
    Vector3
} from 'three';

import * as THREE from 'three';
import { MeshBuilder } from './Utils';

const _plane = new Plane();
const _raycaster = new Raycaster();

const _pointer = new Vector2();
const _offset = new Vector3();
const _intersection = new Vector3();
const _worldPosition = new Vector3();
const _inverseMatrix = new Matrix4();

var statrX = 0;
var selected = false;

const _intersections = new Array<THREE.Intersection<THREE.Object3D>>();

export class BarControls extends EventDispatcher {
    enabled = true;
    recursive = true;

    selected = new THREE.Group();
    objects = new THREE.Group();
    _camera: THREE.Camera;
    sceneBar: THREE.Scene;
    bar: THREE.Mesh;
    onSelect?: (obj: THREE.Object3D) => void;

    constructor(private _domElement: HTMLCanvasElement) {
        super();
        _domElement.style.touchAction = 'none'; // disable touch scroll
        //

        const aspect = _domElement.width / _domElement.height;

        const cameraBar = new THREE.OrthographicCamera(-1, 1, aspect, -aspect, 0.1, 100);
        cameraBar.position.set(0, 0, 5);

        this._camera = cameraBar;

        const sceneBar = new THREE.Scene();
        sceneBar.add(new THREE.HemisphereLight(0xaaaaaa, 0x444444, 3));
        const light = new THREE.DirectionalLight(0xffffff, 1.5);
        light.position.set(10, 10, 10);
        sceneBar.add(light);
        this.objects.scale.divideScalar(4);
        if (aspect > 1) {
            this.objects.position.setY(-aspect * 3 / 5);
        }
        sceneBar.add(this.objects);

        this.bar = MeshBuilder.bar(aspect);
        sceneBar.add(this.bar);
        this.sceneBar = sceneBar;
        cameraBar.lookAt(sceneBar.position);
        this.activate();
    }

    activate() {
        let _domElement = this._domElement;
        _domElement.addEventListener('pointermove', this.onPointerMove.bind(this));
        _domElement.addEventListener('pointerdown', this.onPointerDown.bind(this));
        _domElement.addEventListener('pointerup', this.onPointerCancel.bind(this));
        _domElement.addEventListener('pointerleave', this.onPointerCancel.bind(this));

    }
    dispose() {
        let _domElement = this._domElement;
        _domElement.removeEventListener('pointermove', this.onPointerMove.bind(this));
        _domElement.removeEventListener('pointerdown', this.onPointerDown.bind(this));
        _domElement.removeEventListener('pointerup', this.onPointerCancel.bind(this));
        _domElement.removeEventListener('pointerleave', this.onPointerCancel.bind(this));
        _domElement.style.cursor = '';
    }

    updatePointer(event: PointerEvent) {
        const rect = this._domElement.getBoundingClientRect();
        _pointer.x = (event.clientX - rect.left) / rect.width * 2 - 1;
        _pointer.y = - (event.clientY - rect.top) / rect.height * 2 + 1;

    }
    onPointerMove(event: PointerEvent) {
        let scope = this;
        if (scope.enabled === false) return;
        this.updatePointer(event);
        _raycaster.setFromCamera(_pointer, this._camera);
        if (selected) {
            if (_raycaster.ray.intersectPlane(_plane, _intersection)) {
                let mv = _intersection.sub(_offset).applyMatrix4(_inverseMatrix);
                this.objects.position.setX(statrX + mv.x)
            }
            return;
        }
    }

    onPointerDown(event: PointerEvent) {
        let scope = this;
        if (scope.enabled === false) return;
        this.updatePointer(event);
        let _camera = this._camera;
        let _objects = this.objects;
        _intersections.length = 0;
        _raycaster.setFromCamera(_pointer, _camera);
        _raycaster.intersectObjects(_objects.children, scope.recursive, _intersections);

        selected = _raycaster.intersectObject(this.bar).length > 0;
        if (selected) {
            statrX = this.objects.position.x;
            _plane.setFromNormalAndCoplanarPoint(_camera.getWorldDirection(_plane.normal), _worldPosition.setFromMatrixPosition(this.bar.matrixWorld));
            if (_raycaster.ray.intersectPlane(_plane, _intersection) && this.bar.parent) {
                _inverseMatrix.copy(this.bar.parent.matrixWorld).invert();
                _offset.copy(_intersection).sub(_worldPosition.setFromMatrixPosition(this.bar.matrixWorld));
            }
        }
        if (_intersections.length > 0) {
            let obj = _intersections[0].object;
            while (obj.parent && obj.parent != this.objects) obj = obj.parent;
            obj = obj.clone();
            obj.position.set(0, 0, 0);
            obj.scale.multiplyScalar(5);
            this.selected.clear();
            this.selected.add(obj);
            if(this.onSelect) {
                this.onSelect(obj);
            }
        }
    }


    onPointerCancel() {
        let scope = this;
        if (scope.enabled === false) return;
        selected = false;
    }
}